Javascript Courotines
function* test() {
console.log('Hello!');
var x = yield;
console.log('First I got: ' + x);
var y = yield;
console.log('Then I got: ' + y);
}
To initiate an instance of a coroutine you just call the function, which returns a coroutine object:
var tester = test();
So far none of the function will have executed, so we must execute the function until it hits a yield
, by calling:
tester.next(); // prints 'Hello!'
Now tester
is paused at the first yield
, and we can resume the coroutine by sending a value to the yield
where it is paused:
tester.next('a cat'); // prints 'First I got: a cat'
Now tester
is paused at the second yield
, and we can send another value:
tester.next('a dog'); // prints 'Then I got: a dog'
That's about all there is to coroutines—they are functions which can suspend themselves using the yield
keyword, and you can communicate with them by sending values, which will be returned as the value of yield
and resume execution.
The Convenient coroutine
Wrapper
Every time you use a coroutine you always do three things:
- Call the function and store the resulting coroutine object.
- Call
next()
on the coroutine object, to execute until the firstyield
. - After that all you can do is call
next(...)
to run the coroutine and send it values.
We can put all of this functionality into a wrapper function:
function coroutine(f) {
var o = f(); // instantiate the coroutine
o.next(); // execute until the first yield
return function(x) {
o.next(x);
}
}
This sets up the coroutine and returns a function which we can call directly instead of calling next
on the coroutine object.
Using this wrapper, our earlier example becomes:
var test = coroutine(function*() {
console.log('Hello!');
var x = yield;
console.log('First I got: ' + x);
var y = yield;
console.log('Then I got: ' + y);
});
// prints 'Hello!'
test('a dog'); // prints 'First I got: a dog'
test('a cat'); // prints 'Then I got: a cat'
A Coroutine Loop
Our first example coroutine yields twice and then ends. Let's make a never-ending coroutine instead.
Here's a coroutine that never ends, and each time you resume it, it alternates between ticking and tocking:
var clock = coroutine(function*() {
while (true) {
yield;
console.log('Tick!');
yield;
console.log('Tock!');
}
});
clock(); // prints 'Tick!'
clock(); // prints 'Tock!'
clock(); // prints 'Tick!'
Note that in Javascript, calling clock()
with no argument is the same as calling clock(undefined)
, which in this case is fine since this coroutine doesn't even look at the values being sent to it.
Now let's make the clock tick/tock once every second. It's very easy:
setInterval(clock, 1000);
This resumes the coroutine once every 1000ms, and it will tick and tock for eternity.